package cn.yto.print.controller;

import cn.yto.print.config.CaffeineCacheConfig;
import cn.yto.print.config.CustomWindowEvent;
import cn.yto.print.config.PluginDownLoadConfig;
import cn.yto.print.config.ThreadPoolManager;
import cn.yto.print.config.i18n.I18nResourceManager;
import cn.yto.print.config.i18n.LocaleChangeEvent;
import cn.yto.print.constants.AssistantConstants;
import cn.yto.print.enums.MessageTypeEnum;
import cn.yto.print.enums.PluginRegistryEnum;
import cn.yto.print.listener.PluginProcessMonitor;
import cn.yto.print.plugins.PluginInstalledManager;
import cn.yto.print.plugins.PluginMonitorRunnable;
import cn.yto.print.plugins.PluginStatusManager;
import cn.yto.print.plugins.PluginSupplyRunnable;
import cn.yto.print.service.handle.MessageHandler;
import cn.yto.print.system.version.DownloadPrintPlugin;
import cn.yto.print.system.version.KhgjContactManager;
import cn.yto.print.system.version.SystemUpdateChecker;
import cn.yto.print.utils.*;
import com.jfoenix.controls.JFXButton;
import de.felixroske.jfxsupport.FXMLController;
import de.felixroske.jfxsupport.GUIState;
import javafx.animation.PauseTransition;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip;
import javafx.scene.image.ImageView;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.util.Duration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;

import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences;


@Slf4j
@FXMLController
public class IndexController extends BaseController implements Initializable {

    private static final Map<String, AnchorPane> ANCHOR_PANE_MAP = new HashMap<>();
    private static final Preferences PREFS = Preferences.userNodeForPackage(SystemUpdateChecker.class);
    private static final ScheduledExecutorService executorService = ThreadPoolManager.getSingleScheduledExecutor();

    private static final int MAX_MESSAGE_COUNT = 100;
    private final List<Text> messageElements = new ArrayList<>();
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @FXML
    public AnchorPane tikTok;
    @FXML
    public AnchorPane wph;
    @FXML
    public AnchorPane wxsph;
    @FXML
    public AnchorPane dewu;
    @FXML
    public AnchorPane kuaiShou;
    @FXML
    public AnchorPane jd;
    @FXML
    public AnchorPane aiKuCun;
    @FXML
    public AnchorPane pdd;
    @FXML
    public AnchorPane cainiao;
    @FXML
    public AnchorPane xiaoHongShuX;
    @FXML
    public Label hardWareId;
    @FXML
    public AnchorPane anchorPane;
    @FXML
    public JFXButton startAll;
    @FXML
    public Text promptText;
    @FXML
    public Label deviceIdLabel;
    @FXML
    public JFXButton copyButton;
    @FXML
    public TextFlow logTextFlow;
    @FXML
    public ScrollPane logScrollPane;
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        startAll.textProperty().bind(I18N.getStringBinding("print.button.startAll"));
        promptText.textProperty().bind(I18N.getStringBinding("print.prompt.show"));
        deviceIdLabel.textProperty().bind(I18N.getStringBinding("print.footer.device"));
        copyButton.textProperty().bind(I18N.getStringBinding("print.footer.copy"));

        logScrollPane.setContent(logTextFlow);
        logScrollPane.setFitToWidth(true);
        logScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        Platform.runLater(() -> {
            setStageTitle();
            try {
                String machineCode = MachineCodeUtil.generateMachineCode();
                hardWareId.setText(machineCode);
            } catch (Exception e) {
                log.error("获取本机设备码发生异常", e);
            }
            ThreadPoolManager.getExecutorService().execute(KhgjContactManager::firstLogOperate);
        });

        // 初始化插件面板
        PluginRegistryEnum[] platform = {
                PluginRegistryEnum.CN, PluginRegistryEnum.DY, PluginRegistryEnum.WPH,
                PluginRegistryEnum.PDD, PluginRegistryEnum.DW, PluginRegistryEnum.JD,
                PluginRegistryEnum.XHSX, PluginRegistryEnum.KS, PluginRegistryEnum.WXSPH,
                PluginRegistryEnum.AKC
        };
        AnchorPane[] panes = {cainiao, tikTok, wph, pdd, dewu, jd, xiaoHongShuX, kuaiShou, wxsph, aiKuCun};
        for (int i = 0; i < platform.length; i++) {
            PluginRegistryEnum key = platform[i];
            AnchorPane pane = panes[i];
            ANCHOR_PANE_MAP.put(key.getCode(), pane);
            Tooltip tooltip = new Tooltip(I18N.get("print.plugin." + key.getCode()));
            tooltip.setStyle("-fx-font-size: 14px;");
            Tooltip.install(pane, tooltip);
        }

        ThreadPoolManager.getExecutorService().execute(new PluginSupplyRunnable());
        ThreadPoolManager.getExecutorService().execute(new PluginMonitorRunnable());
        GUIState.getStage().addEventHandler(CustomWindowEvent.CLEAR_CACHE, event -> {
            clearConsoleLog();
        });
        // 启动定时心跳检测任务
        schedulePluginHeartbeatCheck();
    }


    private void schedulePluginHeartbeatCheck() {
        Runnable heartbeatTask = this::executeHeartbeatCheckRound;
        executorService.scheduleWithFixedDelay(heartbeatTask, 60L, 2 * 60L, TimeUnit.SECONDS);
    }

    /**
     * 心跳检测
     */
    private void executeHeartbeatCheckRound() {
        log.info("--------------------- >>> 执行打印组件心跳检测任务 <<< ---------------------");
        for (PluginRegistryEnum pluginRegistry : PluginRegistryEnum.values()) {
            Integer openedStatus = PluginStatusManager.getInstance().getPluginStatus().get(pluginRegistry.getCode());
            if (AssistantConstants.OPENED == openedStatus) {
                Integer heartBeatCount = CaffeineCacheConfig.getHeartCache().getIfPresent(pluginRegistry.getCode());
                if (null == heartBeatCount) {
                    IndexController.setPluginStatusChange(pluginRegistry, AssistantConstants.CLOSED);
                    log.error("执行 {} 打印组件心跳检测失败", pluginRegistry.getName());
                    String message = String.format(
                            I18nResourceManager.getInstance().get("index.console.heartbeat"),
                            I18nResourceManager.getInstance().get(pluginRegistry.getLocale())
                    );
                    addConsoleLog(message, MessageTypeEnum.WARNING);
                } else {
                    try {
                        MessageHandler messageHandler = (MessageHandler) applicationContext.getBean(pluginRegistry.getBeanClass());
                        PluginVerisonUtil.sendAgentInfo(messageHandler.getSocketContext().getSession(), pluginRegistry);
                    } catch (Exception e) {
                        log.error("发送 Agent 信息时发生异常 - {}", pluginRegistry.getName(), e);
                    }
                }
            }
        }
    }

    /**
     * 添加控制台日志
     */
    public void addConsoleLog(String message, MessageTypeEnum msgType) {
        Platform.runLater(() -> internalAddConsoleLog(message, msgType));
    }

    private void internalAddConsoleLog(String message, MessageTypeEnum msgType) {
        String time = LocalDateTime.now().format(formatter);
        Text timeText = createTextNode(time + " - ", msgType);
        Text messageText = createTextNode(message + "\n", msgType);
        messageElements.add(timeText);
        messageElements.add(messageText);
        logTextFlow.getChildren().addAll(timeText, messageText);
        checkAndRemoveOldMessages();
        scrollToBottom();
    }

    private Text createTextNode(String content, MessageTypeEnum msgType) {
        Text t = new Text(content);
        t.setFont(Font.font("SimHei", 12));
        t.setFill(msgType.getColor());
        return t;
    }

    private void scrollToBottom() {
        PauseTransition pause = new PauseTransition(Duration.millis(10));
        pause.setOnFinished(event -> logScrollPane.setVvalue(1.0));
        pause.play();
    }

    private void checkAndRemoveOldMessages() {
        while (messageElements.size() > MAX_MESSAGE_COUNT * 2) {
            Text firstTime = messageElements.remove(0);
            Text firstMessage = messageElements.remove(0);
            // 从 TextFlow 中删除
            logTextFlow.getChildren().removeAll(firstTime, firstMessage);
        }
    }

    /**
     * 清空控制台日志
     */
    public void clearConsoleLog() {
        Platform.runLater(() -> {
            logTextFlow.getChildren().clear();
            messageElements.clear();
        });
    }

    public void handleImageClick(MouseEvent mouseEvent) {
        try {
            ImageView imageView = (ImageView) mouseEvent.getSource();
            AnchorPane anchorPane = (AnchorPane) imageView.getParent();
            PluginRegistryEnum registryKeyEnum = PluginRegistryEnum.getPluginByCode(anchorPane.getId());
            if (null == registryKeyEnum) {
                return;
            }
            log.info("点击 [ {} - 打印组件图标 ]", registryKeyEnum.getName());
            // 如果未安装或进程未启动则执行下载/启动的逻辑
            if (!checkInstalledAndProcessStatus(registryKeyEnum, true)) {
                Rectangle rectangle = (Rectangle) anchorPane.getChildren().get(2);
                Node downloadImageView = anchorPane.getChildren().get(3);
                downloadImageView.setVisible(false);
                String message = String.format(I18nResourceManager.getInstance().get("index.console.download"),
                        I18nResourceManager.getInstance().get(registryKeyEnum.getLocale()));
                addConsoleLog(message, MessageTypeEnum.INFO);
                log.info("【{}】打印组件未安装，开始下载安装包！", registryKeyEnum.getName());
                String downloadUrl = PluginDownLoadConfig.getPluginInstallUrl(anchorPane.getId().toLowerCase());
                DownloadPrintPlugin.startDownloadTask(downloadUrl, rectangle);
            }
        } catch (Exception e) {
            log.error("页面点击打印组件图标发生异常", e);
        }
    }

    /**
     * 检查打印组件是否已安装
     */
    private boolean checkInstalledAndProcessStatus(PluginRegistryEnum pluginRegistry, boolean isShowToast) {
        // 先获取安装路径
        String installPath = PluginInstalledUtil.getPluginInstallPath(pluginRegistry);
        if (StringUtils.isEmpty(installPath)) {
            return false;
        }
        boolean isProcessRunning = PluginProcessMonitor.getInstance().isProcessRunning(pluginRegistry.getProcesses());
        if (isProcessRunning) {
            if (isShowToast) {
                Platform.runLater(() -> FxToastUtil.showToast(I18N.get("print.plugin.toast")));
                addConsoleLog(I18N.get("print.plugin.toast"), MessageTypeEnum.WARNING);
            }
        } else {
            log.info("【{}】打印组件已经安装，开始启动程序：{}", pluginRegistry.getName(), installPath);
            addConsoleLog(String.format(I18nResourceManager.getInstance().get("index.console.started"),
                    I18nResourceManager.getInstance().get(pluginRegistry.getLocale())), MessageTypeEnum.INFO);
            if (pluginRegistry.equals(PluginRegistryEnum.KS) || pluginRegistry.equals(PluginRegistryEnum.CN)) {
                PluginInstalledUtil.startUpPlugin(installPath);
            } else {
                PluginInstalledUtil.startUpDeskPlugin(installPath);
            }
        }
        return true;
    }

    /**
     * 一键启动
     */
    public void oneKeyStarted() {
        log.info("点击 [ 一键启动 ] ");
        ThreadPoolManager.getExecutorService().execute(() -> {
            for (PluginRegistryEnum registryKey : PluginRegistryEnum.values()) {
                checkInstalledAndProcessStatus(registryKey, false);
            }
        });
        addConsoleLog(I18N.get("index.console.startAll"), MessageTypeEnum.INFO);
    }

    /**
     * 复制设备码
     */
    public void copyHardwareId(MouseEvent mouseEvent) {
        log.info("点击 [ 复制 - 设备码 ] ");
        String hardwareId = hardWareId.getText();
        Clipboard clipboard = Clipboard.getSystemClipboard();
        ClipboardContent content = new ClipboardContent();
        content.putString(hardwareId);
        clipboard.setContent(content);
        FxToastUtil.showToast(I18N.get("print.footer.toast"));
        addConsoleLog(I18N.get("print.footer.toast"), MessageTypeEnum.INFO);
    }

    @EventListener
    public void updateLocaleEvent(LocaleChangeEvent event) {
        Platform.runLater(() -> {
            setStageTitle();
            for (PluginRegistryEnum pluginRegistry : PluginRegistryEnum.values()) {
                setPluginStatusPane(pluginRegistry);
            }
            PluginRegistryEnum[] platform = {
                    PluginRegistryEnum.CN, PluginRegistryEnum.DY, PluginRegistryEnum.WPH,
                    PluginRegistryEnum.PDD, PluginRegistryEnum.DW, PluginRegistryEnum.JD,
                    PluginRegistryEnum.XHSX, PluginRegistryEnum.KS, PluginRegistryEnum.WXSPH,
                    PluginRegistryEnum.AKC
            };
            AnchorPane[] panes = {cainiao, tikTok, wph, pdd, dewu, jd, xiaoHongShuX, kuaiShou, wxsph, aiKuCun};
            for (int i = 0; i < platform.length; i++) {
                Tooltip tooltip = new Tooltip(I18N.get("print.plugin." + platform[i].getCode()));
                tooltip.setStyle("-fx-font-size: 14px;");
                Tooltip.install(panes[i], tooltip);
            }
        });
        // 清空控制台
        clearConsoleLog();
    }

    /**
     * 设置窗口标题
     */
    private static void setStageTitle() {
        I18nResourceManager i18nResource = I18nResourceManager.getInstance();
        String baseTitle = i18nResource.get("app.show.title");
        String version = System.getProperty(AssistantConstants.APP_VERSION);
        String stageTitle = baseTitle + "  v" + version;
        String isUpgradeNeeded = PREFS.get(AssistantConstants.UPGRADE_NEED_PREF, "No cached data");
        if ("1".equals(isUpgradeNeeded)) {
            GUIState.getStage().setTitle(stageTitle + i18nResource.get("app.show.tooltip"));
        } else {
            GUIState.getStage().setTitle(stageTitle);
        }
    }

    /**
     * 设置插件安装状态展示
     */
    public static void setPluginInstalledShow(PluginRegistryEnum pluginRegistry, int installedStatus) {
        PluginInstalledManager.getInstance().updateInstallStatus(pluginRegistry.getCode(), installedStatus);
        AnchorPane pluginAnchorPane = ANCHOR_PANE_MAP.get(pluginRegistry.getCode());
        if (null == pluginAnchorPane) {
            return;
        }
        Platform.runLater(() -> {
            try {
                Node installImageView = safeGetChild(pluginAnchorPane, 1);
                Node rectangle = safeGetChild(pluginAnchorPane, 2);
                Node downloadImageView = safeGetChild(pluginAnchorPane, 3);
                Platform.runLater(() -> {
                    if (AssistantConstants.INSTALLED == installedStatus) {
                        installImageView.setVisible(true);
                        rectangle.setVisible(false);
                        downloadImageView.setVisible(false);
                    } else {
                        installImageView.setVisible(false);
                        rectangle.setVisible(true);
                        downloadImageView.setVisible(true);
                    }
                });
            } catch (Exception e) {
                log.error("页面设置【{}】打印组件安装状态异常", pluginRegistry.getName(), e);
            }
        });
    }

    private static Node safeGetChild(AnchorPane pane, int index) {
        try {
            return pane.getChildren().get(index);
        } catch (Exception ex) {
            throw ex;
        }
    }

    public static void setPluginStatusChange(PluginRegistryEnum printScoketEnum, int pluginStatus) {
        PluginStatusManager.getInstance().updatePluginStatus(printScoketEnum.getCode(), pluginStatus);
        setPluginStatusPane(printScoketEnum);
    }

    private static void setPluginStatusPane(PluginRegistryEnum printScoketEnum) {
        AnchorPane printAnchorPane = ANCHOR_PANE_MAP.get(printScoketEnum.getCode());
        if (null == printAnchorPane) {
            return;
        }
        Platform.runLater(() -> {
            try {
                for (Node child : new ArrayList<>(printAnchorPane.getChildren())) {
                    if (child instanceof AnchorPane) {
                        setPluginTextStatus(child, printScoketEnum.getCode());
                    }
                }
            } catch (Exception e) {
                log.error("页面设置【{}】打印组件连接状态异常", printScoketEnum.getName(), e);
            }
        });
    }

    /**
     * 设置打印组件状态
     */
    private static void setPluginTextStatus(Node node, String code) {
        I18nResourceManager i18nResource = I18nResourceManager.getInstance();
        AnchorPane statusAnchorPane;
        try {
            statusAnchorPane = (AnchorPane) node;
        } catch (ClassCastException e) {
            return;
        }
        try {
            int status = PluginStatusManager.getInstance().getPluginStatus().get(code);
            if (status == AssistantConstants.OPENED) {
                setPaneStatus(statusAnchorPane, "bg-green", i18nResource.get("print.linked.linked"), "label-green");
            } else {
                setPaneStatus(statusAnchorPane, "bg-red", i18nResource.get("print.linked.unlink"), "label-red");
            }
            if (status == AssistantConstants.OPENED) {
                AnchorPane parent = (AnchorPane) node.getParent();
                parent.getChildren().get(1).setVisible(true);
                parent.getChildren().get(2).setVisible(false);
                parent.getChildren().get(3).setVisible(false);
            }
        } catch (Exception e) {
            setPaneStatus(statusAnchorPane, "bg-red", i18nResource.get("print.linked.unknow"), "label-red");
        }
    }

    private static void setPaneStatus(AnchorPane statusAnchorPane, String bgClass, String labelText, String labelClass) {
        statusAnchorPane.getStyleClass().setAll(bgClass);
        for (Node child : statusAnchorPane.getChildren()) {
            Label label = (Label) child;
            label.setText(labelText);
            label.setLayoutX(6.0);
            label.getStyleClass().setAll(labelClass);
        }
    }

}

